<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name='viewport' content='width=device-width, initial-scale=1.0'>
  <title>OpenLayers - 常见问题（FAQ）</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/components/prism-core.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/plugins/autoloader/prism-autoloader.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/plugins/toolbar/prism-toolbar.min.js"></script>
  <script
    src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
  <link rel="stylesheet" type="text/css"
    href="https://fonts.googleapis.com/css?family=Quattrocento+Sans:400,400italic,700" crossorigin="anonymous">
  <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"
    crossorigin="anonymous">
  <link rel="stylesheet" type="text/css"
    href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.1.2/css/fontawesome.min.css"
    crossorigin="anonymous">
  <link rel="stylesheet" type="text/css"
    href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.1.2/css/solid.css" crossorigin="anonymous">
  <link rel="stylesheet" type="text/css"
    href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.1.2/css/brands.css" crossorigin="anonymous">
  <link rel="stylesheet" type="text/css" href="/theme/ol.css">
  <link rel="stylesheet" type="text/css" href="/theme/site.css">
  <link rel="icon" type="image/svg+xml" href="/theme/img/logo-light.svg" media="(prefers-color-scheme: light)" />
  <link rel="icon" type="image/svg+xml" href="/theme/img/logo-dark.svg" media="(prefers-color-scheme: dark)" />
</head>

<body>
  <header class="navbar navbar-expand-md navbar-dark mb-3 px-3 py-0 fixed-top" role="navigation">
    <a class="navbar-brand" href="/"><img src="/theme/img/logo-dark.svg" width="70" height="70"
        alt="">&nbsp;OpenLayers</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#olmenu"
      aria-controls="olmenu" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>

    <!-- menu items that get hidden below 768px width -->
    <nav class="collapse navbar-collapse" id="olmenu">
      <ul class="nav navbar-nav ms-auto">
        <li class="nav-item dropdown">
          <a class="nav-link dropdown-toggle" href="#" id="docdropdown" role="button" data-bs-toggle="dropdown"
            aria-haspopup="true" aria-expanded="false">Docs</a>
          <div class="dropdown-menu dropdown-menu-end mb-3" aria-labelledby="docdropdown">
            <a class="dropdown-item" href="/doc/">Docs</a>
            <div class="dropdown-divider"></div>
            <a class="dropdown-item" href="/doc/quickstart.html"><i class="fa fa-check fa-fw me-2 fa-lg"></i>快速入门</a>
            <a class="dropdown-item" href="/doc/faq.html"><i class="fa fa-question fa-fw me-2 fa-lg"></i>常见问题</a>
            <a class="dropdown-item" href="/doc/tutorials/"><i class="fa fa-book fa-fw me-2 fa-lg"></i>教程</a>

          </div>
        </li>
        <li class="nav-item"><a class="nav-link" href="/en/latest/examples/">Examples</a></li>
        <li class="nav-item dropdown">
          <a class="nav-link dropdown-toggle" href="#" id="apidropdown" role="button" data-bs-toggle="dropdown"
            aria-haspopup="true" aria-expanded="false">
            <i class="fa fa-sitemap me-1"></i>API
          </a>
          <div class="dropdown-menu dropdown-menu-end mb-3" aria-labelledby="apidropdown">
            <a class="dropdown-item" href="/en/latest/apidoc/"><i class="fa fa-sitemap fa-fw me-2 fa-lg"></i>v8.2.0
              (latest)</a </div>
        </li>
      </ul>
    </nav>
  </header>

  <div class="container">
    <h1 id="frequently-asked-questions-faq">常见问题（FAQ）</h1>
    <p>当用户寻求帮助时，某些问题比其他问题更常见。本文档试图列出一些在<a href="https://stackoverflow.com/questions/tagged/openlayers">Stack
        Overflow</a>等地方经常被问到的问题。</p>
    <!-- <p>如果您认为某个问题（及其答案）应该添加到这里，请随时与我们联系或发送一个增强此文档的pull请求。</p> -->
    <p>目录：</p>
    <ul>
      <li><a href="#what-projection-is-openlayers-using">OpenLayers使用哪种投影？</a></li>
      <li><a href="#how-do-i-change-the-projection-of-my-map">如何更改地图的投影？</a></li>
      <li><a
          href="#why-is-my-map-centered-on-the-gulf-of-guinea-or-africa-the-ocean-null-island">为什么我的地图会显示在几内亚湾（或非洲、海洋、Null
          Island）上？</a></li>
      <li><a href="#why-is-the-order-of-a-coordinate-lonlat-and-not-latlon">为什么坐标的顺序是[lon,lat]，而不是[lat,lon]？</a></li>
      <li><a href="#why-aren-t-there-any-features-in-my-source">为什么我的源中没有任何要素？</a>
      </li>
      <li><a href="#how-do-i-force-a-re-render-of-the-map">如何强制重新渲染地图？</a></li>
      <li><a href="#why-are-my-features-not-found">为什么找不到我的要素？</a></li>
      <li><a href="#why-is-zooming-or-clicking-in-the-map-offinaccurate">为什么缩放或点击不准确？</a></li>
    </ul>
    <div>
      <h2 id="what-projection-is-openlayers-using">OpenLayers使用哪种投影？</h2>
      <p>使用OpenLayers创建的每个地图都有一个视图，每个视图都有一个投影。由于地球是三维的、圆的，而2D地图的视图不是，我们需要一个数学表达式来表示它。这就是投影的用途。</p>
      <p>投影不止一种，而是有很多常见的投影。每种投影都有不同的属性，它准确地表示距离、角度或面积。某些投影更适合世界上不同的地区。</p>
      <p>回到原始问题：OpenLayers能够处理大多数投影。如果您没有明确设置一个投影，您的地图将使用我们的默认设置，即Web Mercator投影（EPSG:3857）。与OpenStreetMap项目和商业产品（如Bing
        Maps或Google Maps）相同的投影也用于此投影。</p>
      <p>如果您想展示整个世界的地图，这种投影是一个很好的选择。如果您想使用OpenStreetMap或Bing瓦片，您可能需要这种投影。</p>
    </div>
    <div>
      <h2 id="how-do-i-change-the-projection-of-my-map">如何更改地图的投影？</h2>
      <p>您想将OpenLayers的默认投影更改为更适合您所在地区或特定数据的投影。</p>
      <p>您可以通过视图属性设置地图的投影。以下是一些示例：</p>
      <pre><code class="language-javascript">import Map from &#39;ol/Map.js&#39;;
import View from &#39;ol/View.js&#39;;

// OpenLayers comes with support for the World Geodetic System 1984, EPSG:4326:
const map = new Map({
  view: new View({
    projection: &#39;EPSG:4326&#39;
    // other view properties like map center etc.
  })
  // other properties for your map like layers etc.
});
</code></pre>
      <pre><code class="language-javascript">import Map from &#39;ol/Map.js&#39;;
import View from &#39;ol/View.js&#39;;
import proj4 from &#39;proj4&#39;;
import {register} from &#39;ol/proj/proj4.js&#39;;
import {get as getProjection} from &#39;ol/proj.js&#39;;

// To use other projections, you have to register the projection in OpenLayers.
// This can easily be done with [http://proj4js.org/](proj4)
//
// By default OpenLayers does not know about the EPSG:21781 (Swiss) projection.
// So we create a projection instance for EPSG:21781 and pass it to
// register to make it available to the library for lookup by its
// code.
proj4.defs(&#39;EPSG:21781&#39;,
  &#39;+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 &#39; +
  &#39;+x_0=600000 +y_0=200000 +ellps=bessel &#39; +
  &#39;+towgs84=660.077,13.551,369.344,2.484,1.783,2.939,5.66 +units=m +no_defs&#39;);
register(proj4);
const swissProjection = getProjection(&#39;EPSG:21781&#39;);

// we can now use the projection:
const map = new Map({
  view: new View({
    projection: swissProjection
    // other view properties like map center etc.
  })
  // other properties for your map like layers etc.
});
</code></pre>
      <p>我们建议您在 <a href="https://epsg.io/">epsg.io</a>上查找您投影的参数（如有效范围）。</p>
    </div>
    <div>
      <h2 id="why-is-my-map-centered-on-the-gulf-of-guinea-or-africa-the-ocean-null-island">为什么我的地图会显示在几内亚湾（或非洲、海洋、Null
        Island）上？</h2>
      <p>如果您已经在地图视图中设置了中心点，但在视觉输出中没有看到真正的变化，那么您可能为地图中心点的坐标提供了错误的（不匹配的）投影。</p>
      <p>由于OpenLayers的默认投影是Web Mercator（如上所述），因此必须以该投影提供中心点的坐标。您的地图可能看起来像这样：</p>
      <pre><code class="language-javascript">import Map from &#39;ol/Map.js&#39;;
  import View from &#39;ol/View.js&#39;;
  import TileLayer from &#39;ol/layer/Tile.js&#39;;
  import OSM from &#39;ol/source/OSM.js&#39;;
  
  const washingtonLonLat = [-77.036667, 38.895];
  const map = new Map({
    layers: [
      new TileLayer({
        source: new OSM()
      })
    ],
    target: &#39;map&#39;,
    view: new View({
      center: washingtonLonLat,
      zoom: 12
    })
  });
  </code></pre>
      <p>这里提供的 <code>[-77.036667, 38.895]</code> 是视图的中心点。但是，由于Web
        Mercator是一个度量投影，您目前告诉OpenLayers的是，中心点应该离<code>[0, 0]</code>有大约77米和39米的距离。在Web Mercator投影中，该坐标位于几内亚湾。</p>
      <p>解决方案很简单：将坐标投影到Web Mercator。OpenLayers有一些有用的实用方法可以帮助您：</p>
      <pre><code class="language-javascript">import Map from &#39;ol/Map.js&#39;;
  import View from &#39;ol/View.js&#39;;
  import TileLayer from &#39;ol/layer/Tile.js&#39;;
  import OSM from &#39;ol/source/OSM.js&#39;;
  import {fromLonLat} from &#39;ol/proj.js&#39;;
  
  const washingtonLonLat = [-77.036667, 38.895];
  const washingtonWebMercator = fromLonLat(washingtonLonLat);
  
  const map = new Map({
    layers: [
      new TileLayer({
        source: new OSM()
      })
    ],
    target: &#39;map&#39;,
    view: new View({
      center: washingtonWebMercator,
      zoom: 8
    })
  });
  </code></pre>
      <p>从版本3.5开始，可以使用<code>fromLonLat()</code> 方法。</p>
      <p>如果您告诉OpenLayers有关自定义投影（如上所述），您可以使用以下方法将WGS84坐标转换为您的投影：</p>
      <pre><code class="language-javascript">import {transform} from &#39;ol/proj.js&#39;;
  // assuming that OpenLayers knows about EPSG:21781, see above
  const swissCoord = transform([8.23, 46.86], &#39;EPSG:4326&#39;, &#39;EPSG:21781&#39;);
  </code></pre>
    </div>
    <div>
      <h2 id="why-is-the-order-of-a-coordinate-lonlat-and-not-latlon">为什么坐标的顺序是[lon,lat]，而不是[lat,lon]？</h2>
      <p>这是由于两种不同且不兼容的惯例。经度和纬度通常按该顺序给出。地图是地球表面的二维表示/投影，其坐标在<a
          href="https://en.wikipedia.org/wiki/Cartesian_coordinate_system">笛卡尔系统</a>的<code>x,y</code>网格中表示。
        由于按照惯例，地图左侧为西，顶部为北，这意味着<code>x</code>代表经度，<code>y</code>代表纬度。
        如上所述，OpenLayers旨在处理所有投影，但默认视图是在投影的笛卡尔坐标系中。因此，如果将坐标处理为笛卡尔<code>x,y</code>系统和<code>lat,lon</code>系统，那么就需要有重复的函数，这是没有意义的。因此，纬度和经度的度数应按笛卡尔方式输入，即<code>lon,lat</code>。
      </p>
      <p>如果您记不住顺序，可以使用英语语言代码 <code>en</code>作为记忆辅助：东在北之前。</p>
      <h4 id="a-practical-example">一个实用的例子</h4>
      <p>所以你想让地图以地球上的某个地方为中心，显然你需要为此获取其坐标。让我们假设你想让地图以奥地利的Schladming为中心。
        前往<a href="https://en.wikipedia.org/wiki/Schladming">Schladming</a>的维基百科页面。右上角有一个链接到<a
          href="https://geohack.toolforge.org/geohack.php?pagename=Schladming&params=47_23_39_N_13_41_21_E_type:city(4565)_region:AT-6">GeoHack</a>，它会告诉你坐标是：
      </p>
      <pre><code>WGS84:
47° 23′ 39″ N, 13° 41′ 21″ E
47.394167, 13.689167
</code></pre>
      <p>因此，下一步将把十进制坐标放入一个数组中，并将其用作中心：</p>
      <pre><code class="language-javascript">import Map from &#39;ol/Map.js&#39;;
import View from &#39;ol/View.js&#39;;
import TileLayer from &#39;ol/layer/Tile.js&#39;;
import OSM from &#39;ol/source/OSM.js&#39;;
import {fromLonLat} from &#39;ol/proj.js&#39;;

const schladming = [47.394167, 13.689167]; // caution partner, read on...
// since we are using OSM, we have to transform the coordinates...
const schladmingWebMercator = fromLonLat(schladming);

const map = new Map({
layers: [
  new TileLayer({
    source: new OSM()
  })
],
target: &#39;map&#39;,
view: new View({
  center: schladmingWebMercator,
  zoom: 9
})
});
</code></pre>
      <p>运行上述示例可能会让你感到惊讶，因为我们没有以奥地利的Schladming为中心，而是以也门的阿比扬为中心（可能也是一个好地方）。那么发生了什么？</p>
      <p>许多人会将坐标数组中的经度和纬度顺序混淆。如果你一开始就弄错了，不要担心，许多OpenLayers开发人员在他们尝试更改地图中心时，必须三思而后行，是先放经度还是先放纬度。</p>
      <p>好的，那么让我们颠倒坐标：</p>
      <pre><code class="language-javascript">import Map from &#39;ol/Map.js&#39;;
import View from &#39;ol/View.js&#39;;
import TileLayer from &#39;ol/layer/Tile.js&#39;;
import OSM from &#39;ol/source/OSM.js&#39;;
import {fromLonLat} from &#39;ol/proj.js&#39;;

const schladming = [13.689167, 47.394167]; // longitude first, then latitude
// since we are using OSM, we have to transform the coordinates...
const schladmingWebMercator = fromLonLat(schladming);

const map = new Map({
layers: [
  new TileLayer({
    source: new OSM()
  })
],
target: &#39;map&#39;,
view: new View({
  center: schladmingWebMercator,
  zoom: 9
})
});
</code></pre>
      <p>Schladming现在正确地显示在地图中心。</p>
      <p>因此，当您在OpenLayers中处理EPSG：4326坐标时，请先将经度放在第一位，然后是纬度。这种行为与我们在OpenLayers 2中的行为相同，并且实际上是有意义的，因为WGS84的自然轴顺序就是如此。</p>
      <p>如果您记不住正确的顺序，只需看一下我们使用的方法名称：<code>fromLonLat</code>；即使在那里我们也暗示我们期望经度在第一位，然后是纬度。</p>
    </div>
    <div>
      <h2 id="why-aren-t-there-any-features-in-my-source">为什么我的源中没有任何要素？</h2>
      <p>假设您要加载一个KML文件并在地图上显示包含的要素。可以使用以下代码：</p>
      <pre><code class="language-javascript">import VectorLayer from &#39;ol/layer/Vector.js&#39;;
import KMLSource from &#39;ol/source/KML.js&#39;;

const vector = new VectorLayer({
  source: new KMLSource({
    projection: &#39;EPSG:3857&#39;,
    url: &#39;data/kml/2012-02-10.kml&#39;
  })
});
</code></pre>
      <p>您可能会问自己KML中有多少功能，可以尝试以下方法：</p>
      <pre><code class="language-javascript">import VectorLayer from &#39;ol/layer/Vector.js&#39;;
import KMLSource from &#39;ol/source/KML.js&#39;;

const vector = new VectorLayer({
  source: new KMLSource({
    projection: &#39;EPSG:3857&#39;,
    url: &#39;data/kml/2012-02-10.kml&#39;
  })
});
const numFeatures = vector.getSource().getFeatures().length;
console.log(&quot;Count right after construction: &quot; + numFeatures);
</code></pre>
      <p>这将记录源中有<code>0</code>个要素。 这是因为KML文件的加载将以异步方式进行。为了尽快获取计数（在文件被获取后，源被填充了要素后），您应该使用 <code>source</code>上的事件监听器函数：

      </p>
      <pre><code class="language-javascript">vector.getSource().on(&#39;change&#39;, function(evt){
  const source = evt.target;
  if (source.getState() === &#39;ready&#39;) {
    const numFeatures = source.getFeatures().length;
    console.log(&quot;Count after change: &quot; + numFeatures);
  }
});
</code></pre>
      <p>这将正确地报告特征的数量，在该特定情况下为<code>1119</code>。 </p>
    </div>
    <div>
      <h2 id="how-do-i-force-a-re-render-of-the-map">如何强制重新渲染地图？</h2>
      <p>U通常，一旦源发生更改（例如加载了远程源），就会自动重新渲染贴图。</p>
      <p>如果您真的想手动触发渲染，可以使用</p>
      <pre><code class="language-javascript">map.render();
</code></pre>
      <p>...或其配套方法</p>
      <pre><code class="language-javascript">map.renderSync();
</code></pre>

    </div>
    <div>
      <h2 id="why-are-my-features-not-found">为什么找不到我的要素？</h2>
      <p>您正在使用 <code>Map#forEachFeatureAtPixel</code> 或者 <code>Map#hasFeatureAtPixel</code>, 但有时它不适用于大图标或标签？
        <em>命中检测</em>只检查距离给定位置一定距离内的特征。对于大图标，功能的实际几何图形可能太远，因此不会考虑。
      </p>
      <p>在这种情况下，设置<code>VectorLayer</code> 的<code>renderBuffer</code> 属性(默认值为100px):</p>
      <pre><code class="language-javascript">import VectorLayer from &#39;ol/layer/Vector.js&#39;;

const vectorLayer = new VectorLayer({
  ...
  renderBuffer: 200
});
</code></pre>
      <p>建议的值是最大符号、线宽或标签的大小。</p>
    </div>
    <div>
      <h2 id="why-is-zooming-or-clicking-in-the-map-offinaccurate">为什么缩放或点击不准确？
      </h2>
      <p>调整容器元素的大小时，OpenLayers不会更新贴图。
        这可能是由于对CSS样式进行渐进式更新或手动调整地图大小造成的。当这种情况发生时，任何交互都会变得不准确：地图会放大和缩小，最终不会以指针为中心。这使得很难进行某些交互，例如选择所需的功能。
      </p>
      <p>由于<a href="https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver">Resize Observer API</a> is only
        仅在Chrome中实现，因此目前还没有内置的方法来对元素的大小变化做出反应。</p>
      <p>T然而，有一种易于使用的 <a href="https://github.com/que-etc/resize-observer-polyfill">polyfill</a>:</p>
      <pre><code class="language-javascript">import Map from &#39;ol/Map.js&#39;;
import ResizeObserver from &#39;resize-observer-polyfill&#39;;

const mapElement = document.querySelector(&#39;#map&#39;)
const map = new Map({
  target: mapElement
})

const sizeObserver = new ResizeObserver(() =&gt; {
  map.updateSize()
})
sizeObserver.observe(mapElement)

// called when the map is destroyed
// sizeObserver.disconnect()
</code></pre>

    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"></script>
</body>

</html>